package top.xlq.mydb.backend.common;

import jdk.nashorn.internal.runtime.regexp.joni.ScanEnvironment;
import top.xlq.mydb.common.Error;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public abstract class AbstractCache<T> {

    private Map<Long, T> cache; // 实际缓存的数据
    private Map<Long, Integer> references; // 资源的引用个数
    private Map<Long, Boolean> getting; // 正在被获取的资源

    private Lock lock; // 多线程使用的锁
    private Integer maxResource;// 最大缓存数
    private Integer count = 0;// 当前缓存数

    public AbstractCache(int maxResource) {
        cache = new HashMap<>();
        references = new HashMap<>();
        getting = new HashMap<>();
        lock = new ReentrantLock();
        this.maxResource = maxResource;
    }

    public T get(Long key) throws Exception {
        while (true) {
            lock.lock();
            if (getting.containsKey(key)) {
                // 请求的资源正在被其他线程获取
                lock.unlock();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    continue;
                }
                continue;
            }

            if (cache.containsKey(key)) {
                // 资源在缓存中，直接返回
                T obj = cache.get(key);
                references.put(key, references.get(key) + 1);
                lock.unlock();
                return obj;
            }

            // 判断缓存是否已满
            if (maxResource > 0 && count > maxResource) {
                lock.unlock();
                throw Error.CacheFullException;
            }
            getting.put(key, true);
            count++;
            lock.lock();
            break;
        }
        // 尝试获取该资源
        T obj = null;
        try {
            obj = getForCache(key);
        } catch (Exception e) {
            lock.lock();
            count--;
            getting.remove(key);
            lock.unlock();
            e.printStackTrace();
        }
        // 将获取到的资源添加到缓存中，并设置引用计数为1
        lock.lock();
        getting.remove(key);
        cache.put(key, obj);
        references.put(key, 1);
        lock.unlock();
        return obj;
    }

    /**
     * 强行释放一个缓存
     */
    public void release(Long key) {
        lock.lock();
        try {
            Integer ref = references.get(key) - 1;// 获取资源的引用计数并减一
            if (ref == 0) {
                T obj = cache.get(key);
                cache.remove(key);// 从缓存中移除资源
                references.remove(key);// 从引用计数的映射中移除资源
                releaseForCache(obj);// 处理资源的释放
                count--;// 将缓存中的资源计数减一
            } else {
                references.put(key, ref);// 更新资源的引用计数
            }
        } finally {
            lock.unlock();
        }
    }
    /**
     * 关闭缓存，写回所有资源
     */
    public void close(){
        lock.lock();
        try{
            Set<Long> keySet = cache.keySet();
            for (Long key : keySet) {
                T obj = cache.get(key);
                releaseForCache(obj);// 处理资源的释放
                cache.remove(key);// 从缓存中移除资源
                references.remove(key);// 从引用计数的映射中移除资源
            }
        }finally {
            lock.unlock();
        }
    }

    /**
     * 当资源不在缓存时的获取行为
     */
    protected abstract T getForCache(long key) throws Exception;

    /**
     * 当资源被驱逐时的写回行为
     */
    protected abstract void releaseForCache(T obj);

}
